import { _decorator, Label, Asset, native, loader } from 'cc';
import { BCGameLog } from '../utils/BCGameLog';

export class BCHotUpdateMgr {
	private m_lblVersion: Label = null;

	private m_storagePath: string = "";
	private m_assetsManager: native.AssetsManager = null;
	private m_updating: boolean = false;

	private m_alreadyNewVersionCb: () => void = null;
	private m_hasNewVersionCb: (totalByte: number) => void = null;
	private m_checkErrorCb: () => void = null;
	private m_updateErrorCb: () => void = null;
	private m_updateCompleteCb: () => void = null;
	private m_updateProgressCb: (percent: number, currByte: number, totalByte: number) => void = null;

	set alreadyNewVersionCb(cb: () => void) {
		this.m_alreadyNewVersionCb = cb;
	}

	private runAlreadyNewVersionCb() {
		if (!this.m_alreadyNewVersionCb) {
			BCGameLog.warn("[HotUpdateMgr]", "未找到alreadyNewVersion方法！");
			return;
		}
		this.m_alreadyNewVersionCb();
	}

	set hasNewVersionCb(cb: (totalByte: number) => void) {
		this.m_hasNewVersionCb = cb;
	}

	private runHasNewVersionCb(totalByte: number) {
		if (!this.m_hasNewVersionCb) {
			BCGameLog.warn("[HotUpdateMgr]", "未找到hasNewVersion方法！");
			return;
		}
		this.m_hasNewVersionCb(totalByte);
	}

	set checkErrorCb(cb: () => void) {
		this.m_checkErrorCb = cb;
	}

	private runCheckErrorCb() {
		if (!this.m_checkErrorCb) {
			BCGameLog.warn("[HotUpdateMgr]", "未找到checkError方法！");
			return;
		}
		this.m_checkErrorCb();
	}

	set updateErrorCb(cb: () => void) {
		this.m_updateErrorCb = cb;
	}

	private runUpdateErrorCb() {
		if (!this.m_updateErrorCb) {
			BCGameLog.warn("[HotUpdateMgr]", "未找到updateError方法！");
			return;
		}
		this.m_updateErrorCb();
	}

	set updateCompleteCb(cb: () => void) {
		this.m_updateCompleteCb = () => {
			const searchPaths = native.fileUtils.getSearchPaths();
			const newPaths = this.m_assetsManager.getLocalManifest().getSearchPaths();
			Array.prototype.unshift.apply(searchPaths, newPaths);
			localStorage.setItem('HotUpdateSearchPaths', JSON.stringify(searchPaths));
			native.fileUtils.setSearchPaths(searchPaths);
			cb();
		};
	}

	private runUpdateCompleteCb() {
		if (!this.m_updateCompleteCb) {
			BCGameLog.warn("[HotUpdateMgr]", "未找到updateComplete方法！");
			return;
		}
		this.m_updateCompleteCb();
	}

	set updateProgressCb(cb: (percent: number, currByte: number, totalByte: number) => void) {
		this.m_updateProgressCb = cb;
	}

	private runUpdateProgressCb(percent: number, currByte: number, totalByte: number) {
		if (!this.m_updateProgressCb) {
			BCGameLog.warn("[HotUpdateMgr]", "未找到updateProgress方法！");
			return;
		}
		this.m_updateProgressCb(percent, currByte, totalByte);
	}

	constructor(versionLabel: Label | null, manifest?: Asset) {
		this.m_lblVersion = versionLabel;

		this.m_storagePath = ((native.fileUtils ? native.fileUtils.getWritablePath() : '/') + 'remote-asset/');
		BCGameLog.log("[HotUpdateMgr]", this.m_storagePath);

		this.m_assetsManager = new native.AssetsManager('', this.m_storagePath, this.compareVersion.bind(this));

		const localManifestPath = this.m_storagePath + `/project.manifest`;
		let jsbGameManifest: native.Manifest = null;
		if (native.fileUtils.isFileExist(localManifestPath)) {
			BCGameLog.log("[HotUpdateMgr]", "本地存在更新记录！");
			const content = native.fileUtils.getStringFromFile(localManifestPath);
			jsbGameManifest = new native.Manifest(content, this.m_storagePath);
			this.m_assetsManager.loadLocalManifest(jsbGameManifest, "");
		} else {
			BCGameLog.log("[HotUpdateMgr]", "本地不存在更新记录！");
			if(manifest){
				BCGameLog.log("[HotUpdateMgr]", "项目存在更新清单！");
				if (this.m_assetsManager.getState() === native.AssetsManager.State.UNINITED) {
					var url = manifest.nativeUrl;
					if (loader.md5Pipe) {
						url = loader.md5Pipe.transformURL(url);
					}
					BCGameLog.log("[HotUpdateMgr]", url);
					
					this.m_assetsManager.loadLocalManifest(url);
					jsbGameManifest = this.m_assetsManager.getLocalManifest();
				}
			} else {
				BCGameLog.log("[HotUpdateMgr]", "项目不存在更新清单，跳过更新");
				this.runAlreadyNewVersionCb();
				return;
			}
		}
		this.m_lblVersion && (this.m_lblVersion.string = "version:" + jsbGameManifest.getVersion());
		this.m_assetsManager.setVerifyCallback(function (path, asset) {
			return true;
		}.bind(this));
	}

	private compareVersion(localVersion: string, serverVersion: string) {
		BCGameLog.log("[HotUpdateMgr]", `LocalVersion is:${localVersion}, ServerVersion is:'${serverVersion}`);
		const vA = localVersion.split('.');
		const vB = serverVersion.split('.');
		for (let i = 0; i < vA.length; ++i) {
			const a = parseInt(vA[i]);
			var b = parseInt(vB[i]);
			if (a === b) {
				continue;
			} else {
				return a - b;
			}
		}

		if (vB.length > vA.length) {
			return -1;
		} else {
			return 0;
		}
	}

	checkUpdate() {
		if (this.m_updating) {
			return;
		}
		this.m_updating = true;
		BCGameLog.log("[HotUpdateMgr]", this.m_assetsManager.getLocalManifest()?.getVersionFileUrl());
		this.m_assetsManager.setEventCallback(this.checkUpdateCb.bind(this));
		this.m_assetsManager.checkUpdate();
	}

	private checkUpdateCb(event: jsb.EventAssetsManager) {
		let isEnd: boolean = false;
		let isNeedUpdate: boolean = false;
		switch (event.getEventCode()) {
			case native.EventAssetsManager.ERROR_NO_LOCAL_MANIFEST:
				BCGameLog.warn("[HotUpdateMgr]", "CheckUpdateGameResult: ERROR_NO_LOCAL_MANIFEST");
				isEnd = true;
				isNeedUpdate = true;
				break;
			case native.EventAssetsManager.ERROR_DOWNLOAD_MANIFEST:
				BCGameLog.warn("[HotUpdateMgr]", "CheckUpdateGameResult: ERROR_DOWNLOAD_MANIFEST");
				this.runCheckErrorCb();
				isEnd = true;
				break;
			case native.EventAssetsManager.ERROR_PARSE_MANIFEST:
				BCGameLog.warn("[HotUpdateMgr]", "CheckUpdateGameResult: ERROR_PARSE_MANIFEST");
				this.runCheckErrorCb();
				isEnd = true;
				break;
			case native.EventAssetsManager.UPDATE_PROGRESSION:
				BCGameLog.log("[HotUpdateMgr]", "CheckUpdateGameResult: UPDATE_PROGRESSION");
				break;
			case native.EventAssetsManager.ALREADY_UP_TO_DATE:
				BCGameLog.log("[HotUpdateMgr]", "CheckUpdateGameResult: ALREADY_UP_TO_DATE");
				this.runAlreadyNewVersionCb();
				isEnd = true;
				break;
			case native.EventAssetsManager.NEW_VERSION_FOUND:
				BCGameLog.log("[HotUpdateMgr]", "CheckUpdateGameResult: NEW_VERSION_FOUND");
				isEnd = true;
				isNeedUpdate = true;
				break;
			default:
				BCGameLog.log("[HotUpdateMgr]", "CheckUpdateGameResult: " + event.getEventCode());
				break;
		}

		if (isEnd) {
			this.m_assetsManager.setEventCallback(null);
			this.m_updating = false;
			if (isNeedUpdate) {
				this.runHasNewVersionCb(event.getTotalBytes());
			}
		}
	}

	startUpdate() {
		if (this.m_updating) {
			return;
		}
		this.m_updating = true;
		this.m_assetsManager.setEventCallback(this.updateCb.bind(this));
		this.m_assetsManager.update();
	}

	private updateCb(event: jsb.EventAssetsManager) {
		let isEnd: boolean = false;
		switch (event.getEventCode()) {
			case native.EventAssetsManager.ERROR_NO_LOCAL_MANIFEST:
				BCGameLog.warn("[HotUpdateMgr]", "UpdateGameResult: ERROR_NO_LOCAL_MANIFEST");
				this.runUpdateErrorCb();
				isEnd = true;
				break;
			case native.EventAssetsManager.UPDATE_PROGRESSION:
				this.runUpdateProgressCb(event.getPercent(), event.getDownloadedBytes(), event.getTotalBytes());
				break;
			case native.EventAssetsManager.ERROR_DOWNLOAD_MANIFEST:
				BCGameLog.warn("[HotUpdateMgr]", "UpdateGameResult: ERROR_DOWNLOAD_MANIFEST");
				this.runUpdateErrorCb();
				isEnd = true;
				break;
			case native.EventAssetsManager.ERROR_PARSE_MANIFEST:
				BCGameLog.warn("[HotUpdateMgr]", "UpdateGameResult: ERROR_PARSE_MANIFEST");
				this.runUpdateErrorCb();
				isEnd = true;
				break;
			case native.EventAssetsManager.ALREADY_UP_TO_DATE:
				BCGameLog.log("[HotUpdateMgr]", "UpdateGameResult: ALREADY_UP_TO_DATE");
				this.runAlreadyNewVersionCb();
				isEnd = true;
				break;
			case native.EventAssetsManager.UPDATE_FINISHED:
				BCGameLog.log("[HotUpdateMgr]", "UpdateGameResult: UPDATE_FINISHED");
				this.runUpdateCompleteCb();
				isEnd = true;
				break;
			case native.EventAssetsManager.UPDATE_FAILED:
				BCGameLog.warn("[HotUpdateMgr]", "UpdateGameResult: UPDATE_FAILED");
				this.runUpdateErrorCb();
				isEnd = true;
				break;
			case native.EventAssetsManager.ERROR_UPDATING:
				BCGameLog.warn("[HotUpdateMgr]", "UpdateGameResult: ERROR_UPDATING");
				this.runUpdateErrorCb();
				isEnd = true;
				break;
			case native.EventAssetsManager.ERROR_DECOMPRESS:
				BCGameLog.warn("[HotUpdateMgr]", "UpdateGameResult: ERROR_DECOMPRESS");
				this.runUpdateErrorCb();
				isEnd = true;
				break;
			default:
				break;
		}

		if (isEnd) {
			this.m_assetsManager.setEventCallback(null);
			this.m_updating = false;
		}
	}
}

